*IAT SCORING ALGORITHM FROM GREENWALD, NOSEK, & BANAJI, 2002
LAST UPDATE: 03/03/04

This macro will transform a datafile with raw trial latencies (stored as one line per response) 
for a standard format IAT (7 blocks) into a one line summary per subject of the IAT effect using
GNB's new scoring algorithm.

To use this algorithm for your SAS program, perform these two steps:  
(1) Run this script.  The macro should then be loaded into SAS's active memory.  
(2) In your SAS program, enter the following statement 

%iatCalc(libIn, libOut, indata, outdata, BLOCNAME, SESHID, TRLLATNC, TRLERROR, VERROR, VEXTREME, VSD)

and replace the dummy values with the ones that correspond to your own datafiles and variable names.  


Descriptions of what the macro expects for input, and what it will output are below.  Also, at the 
bottom of this commenting section, there is a sample SAS script that would invoke this macro (assuming 
that you have already run the macro to get it into SAS's active memory).

   The macro expects the following types of libraries, datafiles, and data:
      libIn     = input SAS library name
      libOut    = output SAS library name
      indata    = filename of input SAS dataset in the input SAS library
      outdata   = filename for the output SAS dataset in the output SAS library
      BLOCNAME  = variable name for block identifier in the indata file: alphanumeric indication of the 
                  four trial blocks ('B3', 'B4', 'B6', 'B7' are critical blocks corresponding 
                  to B3, B4, B6, and B7 from GNB, 2002).  At present the macro requires that the variable 
                  passed here uses the names 'B3', 'B4', 'B6', 'B7' to refer to the B3-B7 blocks
	  SESHID    = variable name for unique subject identifier in the indata file
	  TRLLATNC  = variable name for latency of response for trial in the indata file
	  TRLERROR  = variable name for error coding: 0 if initial response was correct, 1 if initial response 
                  was incorrect in the indata file
      VERROR    = value: if '1' the algorithm will use error trial latencies, if '2' the algorithm will replace 
                  error trial latencies with blockmean+600
      VEXTREME  = value: if '1' the algorithm provides no treatment of extreme values, if '2' the algorithm
                  will delete trials <400ms
      VSTD      = value: if '1' the block standard deviation is performed including corrected error trials,
                  if '2' the block standard deviation is performed on correct responses only (1 is standard)

   The macro will output the following variables to the outdata file:
      SESHID  = unique subject identifier
      SUBEXCL = 0 for inclusion data, 1 for excluded data, 2 for incomplete data
      MB3     = mean of trial latencies for B3
      MB4     = mean of trial latencies for B4
      MB6     = mean of trial latencies for B6
      MB7     = mean of trial latencies for B7
      CS1     = standard deviation for B3 and B6 trials combined (correct trials only)
      CS2     = standard deviation for B4 and B7 trials combined (correct trials only)
      AS1     = standard deviation for B3 and B6 trials combined (all trials)
      AS2     = standard deviation for B4 and B7 trials combined (all trials)
      EB3     = percent errors of trials for B3
      EB4     = percent errors of trials for B4
      EB6     = percent errors of trials for B6
      EB7     = percent errors of trials for B7
      FB3     = percent fast responses of trials for B3
      FB4     = percent fast responses of trials for B4
      FB6     = percent fast responses of trials for B6
      FB7     = percent fast responses of trials for B7
      DIFF1   = MB6 - MB3
      DIFF2   = MB7 - MB4
      IAT1    = DIFF1/STD1
      IAT2    = DIFF2/STD2
	  IAT     = mean of IAT1 and IAT2
;

/* SAMPLE SAS SCRIPT THAT USES THIS MACRO ASSUMING THAT IT IS IN ACTIVE SAS MEMORY
This sample script opens two libraries called 'web' and 'outdata'. In the 'web' library a SAS file 
named 'iatrace350000' already exists and will be pulled into active memory by the macro.  When the macro 
finishes it will output the result data into a file in the library 'outdata' called 'Ciatrace350000'. The 
information coding the name of the block is in the variable BLOCK (which is passed to 'BLOCNAME' for
the algorithm). The information coding subject ID is SUB (passed to 'SESHID').  The information coding
the latency of each trial is in LATENCY (passed to TRLLATNC).  The information coding whether an error
was made on each trial is in ERROR (passed to TRLERROR).  And, the setting '2' for VERROR means that 
error latencies will be replaced with the blockmean+600.  The setting '2' for VEXTREME means that responses
faster than 400ms will be deleted before calculating the IAT effect. The setting '1' for VSTD means that 
the standard deviations will be calculated after recombining error trials so the range of D values is bounded 
between (-2,2).  When the algorithm finishes and saves the output, the proc means statement will calculate a 
mean of the IAT effects across subjects (after excluding 'bad' subjects).

libname web 'H:\raceatt\';
libname outdata 'H:\raceattclean\';
%iatCalc(web, outdata, iatrace350000, Ciatrace350000, BLOCK, SUB, LATENCY, ERROR, 2, 2,1);
proc means (data=outdata.Ciatrace350000);var IAT;where SUBEXCL=0;run;
*/

/*MACROS for NEW IMPLICIT ASSOCIATION TEST SCORING ALGORITHM (Greenwald, Nosek, & Banaji, 2002)*/
%macro iatCalc(libIn, libOut, indata, outdata, BLOCNAME, SESHID, TRLLATNC, TRLERROR, VERROR, VEXTREME, VSTD);
%let divide= /; %let multiply= *; %let add = +; %let subtract = -;
%iatAlgorithm(libIn=&libIn, libOut=&libOut, indata=&indata, outdata=&outdata, BLOCNAME=&BLOCNAME, 
              SESHID=&SESHID, TRLLATNC=&TRLLATNC, TRLERROR=&TRLERROR, VERROR=&VERROR, VEXTREME=&VEXTREME, VSTD=&VSTD);run;
%mend iatCalc;

%macro iatAlgorithm(libIn, libOut, indata, outdata, BLOCNAME, SESHID, TRLLATNC, TRLERROR, VERROR, VEXTREME, VSTD);
data IAT; set &libIn..&indata; 
%*PRELIMINARY STEPS FOR HANDLING WEBDATA FORMATS;
    keep &BLOCNAME &SESHID &TRLLATNC &TRLERROR;
proc sort data=iat; by &SESHID &BLOCNAME;	
	%*options nonotes; %*suppress all Notes to log;	

data IAT; set IAT;

%*STEP 1: Include data from B3, B4, B6, B7;
   if &BLOCNAME in ('B3', 'B4', 'B6', 'B7') then ;
   else delete;

%*STEP 2a: Eliminate trial latencies > 10,000ms;
   if &TRLLATNC > 10000 then delete;

%*STEP 2b: Eliminate subjects for whom more than 10% of trials have latencies < 300ms;
   else if &TRLLATNC < 0 then delete; %*for miscoded data in datafile indicating negative response times;
   else if -1 < &TRLLATNC < 300 then FAST = 1;
   else FAST = 0;

data FASTDATA; set IAT; keep &SESHID &BLOCNAME FAST;
proc sort; by &SESHID &BLOCNAME;
proc means data=IAT noprint; by &SESHID &BLOCNAME; var FAST; output out=means mean=MEAN;
proc transpose data=means prefix=F name=name out=FASTMEAN; by &SESHID; id &BLOCNAME;
data FASTMEAN; set FASTMEAN; where name='MEAN'; FASTM = mean(FB3, FB4, FB6, FB7);
   if FASTM > .10 then SUBEXCL = 1; else SUBEXCL = 0; 
   %*SUBEXCL = 0 (include data), 1 (exclude data - too many fast responses), 2 (exclude data - missing data);
   %*The SUBEXCL variable needed to be reintroduced to the final dataset in STEP 12;

%*STEP 3: Use all trials;
   %*in the conventional algorithm the first two trials of each block would be dropped here;

%*STEP 4: No extreme value treatment <or> delete trial with latencies <400ms;
data IAT; set IAT; 
    %*if &VEXTREME = 1 then do nothing here;
    if &VEXTREME = 2 then do; if &TRLLATNC < 400 then delete; end;

proc sort data=IAT; by &SESHID &BLOCNAME;

%*STEP 5: Compute mean of correct latencies for each block;
data CORR; set IAT;
   %*if &VERROR is 1 then means and SDs will be calculated for the entire set of latencies;
   if &VERROR = 2 then do; if &TRLERROR NE 0 then delete; end;
   keep &SESHID &BLOCNAME &TRLLATNC &TRLERROR;
proc means data=CORR noprint; by &SESHID &BLOCNAME; var &TRLLATNC; output out=means mean=MEAN;
proc transpose data=means prefix=M name=name out=CORRMEAN; by &SESHID; id &BLOCNAME;
data CORRMEAN; set CORRMEAN; where name='MEAN';

%*STEP 6a: Compute pooled SD for B3 & B6, and separately for B4 & B7 for correct trials only;
data SD; set CORR;
   if &TRLERROR NE 0 then delete;  *drop error trials;
   if &BLOCNAME in ('B3', 'B6') then TD = '1';
   else if &BLOCNAME in ('B4', 'B7') then TD = '2';
   else delete;
   drop &BLOCNAME;
proc sort data=SD; by &SESHID TD;
proc means data=SD noprint; by &SESHID TD; var &TRLLATNC; output out=means std=STD;
proc transpose data=means prefix=CS name=name out=CORRSTD2; by &SESHID; id TD;
data CORRSTD2; set CORRSTD2; where name='STD';

%*STEP 6b: Compute pooled SD for B3 & B6, and separately for B4 & B7 including error trials;
data SD; merge IAT CORRMEAN; by &SESHID;
   if &TRLERROR < 0 then delete;
   else if &TRLERROR > 1 then delete; %*get rid of coding errors;
   else if &TRLERROR = 1 and &VERROR = 2 then do;
      if &BLOCNAME in ('B3') then &TRLLATNC = MB3 + 600;
	  else if &BLOCNAME in ('B4') then &TRLLATNC = MB4 + 600;
	  else if &BLOCNAME in ('B6') then &TRLLATNC = MB6 + 600;
	  else if &BLOCNAME in ('B7') then &TRLLATNC = MB7 + 600;
   end;
   if &BLOCNAME in ('B3', 'B6') then TD = '1';
   else if &BLOCNAME in ('B4', 'B7') then TD = '2';
   else delete;
   drop &BLOCNAME;
proc sort data=SD; by &SESHID TD;
proc means data=SD noprint; by &SESHID TD; var &TRLLATNC; output out=means std=STD;
proc transpose data=means prefix=AS name=name out=CORRSTD1; by &SESHID; id TD;
data CORRSTD1; set CORRSTD1; where name='STD';

%*STEP 7: Replace error latencies with block mean + 600ms 
<or> use latency from stimulus onset to correct response (when correct response is required);
data ERR; set IAT;
   keep &SESHID &BLOCNAME &TRLERROR;
   if &TRLERROR < 0 then delete;
   else if &TRLERROR > 1 then delete; %*get rid of coding errors;
proc means data=ERR noprint; by &SESHID &BLOCNAME; var &TRLERROR; output out=means mean=MEAN;
proc transpose data=means prefix=E name=name out=ERRMEAN; by &SESHID; id &BLOCNAME;
data ERRMEAN; set ERRMEAN; where name='MEAN';

%*STEP 7 continued: combining data;
data COMBINE; merge CORRMEAN CORRSTD1 CORRSTD2 ERRMEAN FASTMEAN; by &SESHID; %*combining datasets for calculating final means;
   if &VERROR=2 then do;
      array BLOCKMeans(*) MB: ;
      array BLOCKCstds(*) CS: ;
	  array BLOCKAstds(*) AS: ;
      array BLOCKErrs(*) EB: ;
      do i=1 to dim(BLOCKMeans); %*for each of the four blocks replace error trials with mean + 600ms;
	     BLOCKMeans{i} = (1-BLOCKErrs{i})*BLOCKMeans{i} + (BLOCKErrs{i})*(BLOCKMeans{i}+600);			
      end;
   end;

%*STEP 8: No transformation of latencies;
    %*in the conventional algorithm, raw latencies would be log transformed prior to the transposing in the current format;

%*STEP 9: Average latencies for each of the four blocks;
    %*this step was already accomplished in the do loop above;

%*STEP 10: Compute two differences B6-B3 and B7-B4 (does not account for pairing order);
   DIFF1 = MB6 - MB3;
   DIFF2 = MB7 - MB4;

%*STEP 11: Divide each difference by associated pooled SD from STEP 6a or 6b;
   If &VSTD = 2 then do;
      IAT1 = DIFF1/CS1;
      IAT2 = DIFF2/CS2;
   end;
   else do; %*IF VSTD = 1 also set as default;
      IAT1 = DIFF1/AS1;
	  IAT2 = DIFF2/AS2;
   end; 

%*STEP 12: Average quotients from STEP 11;
   IAT = mean(IAT1, IAT2);

%*if there is missing data, do mark data as excluded (SUBEXCL=2);
   do i=1 to dim(BLOCKMeans);
      if BLOCKMeans{i} = . then SUBEXCL = 2;
   end;
data &libout..&outdata (drop=i name);
	set COMBINE;
run;
%mend iatAlgorithm;
*END OF ALGORITHM;

libname study 'C:\primary\Data\Dissertation.2001\Diss.5\';
data comb1;
  informat Code $18.;informat Bname $18.;infile 'C:\primary\Data\Dissertation.2001\Diss.5\disssc.dat' delimiter='09'x;
  input date    time $ build sub     code $  block   trial   lat     resp    corr    stim1 bname $;
  if bname in ('bcode') then delete;
data comb2;
  informat Code $18.;informat Bname $18.;infile 'C:\primary\Data\Dissertation.2001\Diss.5\dissse.dat' delimiter='09'x;
  input date    time $ build sub     code $  block   trial   lat     resp    corr    stim1 bname $;
  if bname in ('bcode') then delete;
data comb3;
  informat Code $18.;informat Bname $18.;infile 'C:\primary\Data\Dissertation.2001\Diss.5\disssf.dat' delimiter='09'x;
  input date    time $ build sub     code $  block   trial   lat     resp    corr    stim1 bname $;
  if bname in ('bcode') then delete;
data comb4;
  informat Code $18.;informat Bname $18.;infile 'C:\primary\Data\Dissertation.2001\Diss.5\disssd.dat' delimiter='09'x;
  input date    time $ build sub     code $  block   trial   lat     resp    corr    stim1 bname $;
  if bname in ('bcode') then delete;
data comb5;
  informat Code $18.;informat Bname $18.;infile 'C:\primary\Data\Dissertation.2001\Diss.5\rest.dat' delimiter='09'x;
  input date    time $ build sub     code $  block   trial   lat     resp    corr    stim1 bname $;
  if bname in ('bcode') then delete;
data comb; set comb1 comb2 comb3 comb4 comb5;
  if sub < 100 then delete;
  drop build time date; 
  if resp = 16 then delete; /*control quit*/
  err = 1 - corr;

  if bname in ('bcode', 'boggle1', 'boggle2', 'boggle3', 'interview', 'badgood', 'flowersinsects', 'insectsflowers') then delete;

/*proc means;class bname; var lat;run;
proc means; class code;var lat;run;
proc means; class sub; var corr;run;
proc means mean std; class sub bname; var corr;run;
proc means; class block;var lat;run;*/

proc sort; by sub;
data study.raw; set comb;

data study.iFI; set study.raw;
  if bname = 'flowersgood' and trial < 22 then blockn = 'B3';
  else if bname = 'flowersgood' and trial > 22 then blockn = 'B4';
  else if bname = 'flowersbad' and trial < 22 then blockn = 'B6';
  else if bname = 'flowersbad' and trial > 22 then blockn = 'B7';
  else delete;
%iatCalc(study, study, iFI, iFIclean, blockn, SUB, LAT, err, 2, 2, 1);
data study.iFIclean; set study.iFIclean;
  FIiat = IAT; FIiat1 = IAT1; FIiat2 = IAT2;
  FIiatexcl = subexcl; FItradIAT = DIFF2;
  FImb3 = mb3; FImb4 = mb4; FImb6 = mb6; FImb7 = mb7; 
  FIeb3 = eb3; FIeb4 = eb4; FIeb6 = eb6; FIeb7 = eb7; 
  if FIeb3>.39 | FIeb4>.39 | FIeb6>.39 | FIeb7>.39 then FIiatexcl=1;
  if FIiatexcl > 0 then do; FIiat = .; FIiat1 = .; FIiat2 = .; FItradIAT = .; end;
  drop IAT IAT1 IAT2 subexcl mb3 mb4 mb6 mb7 CS1 CS2 AS1 AS2 EB3 EB4 EB6 EB7 FB3 FB4 FB6 FB7 FASTM DIFF1 DIFF2;
*proc means;run;

data study.iDR; set study.raw;
  if bname = 'democratgood' and trial < 22 then blockn = 'B3';
  else if bname = 'democratgood' and trial > 22 then blockn = 'B4';
  else if bname = 'democratbad' and trial < 22 then blockn = 'B6';
  else if bname = 'democratbad' and trial > 22 then blockn = 'B7';
  else delete;
%iatCalc(study, study, iDR, iDRclean, blockn, SUB, LAT, err, 2, 2, 1);
data study.iDRclean; set study.iDRclean;
  DRiat = IAT; DRiat1 = IAT1; DRiat2 = IAT2;
  DRiatexcl = subexcl; DRtradIAT = DIFF2;
  DRmb3 = mb3; DRmb4 = mb4; DRmb6 = mb6; DRmb7 = mb7; 
  DReb3 = eb3; DReb4 = eb4; DReb6 = eb6; DReb7 = eb7; 
  if DReb3>.39 | DReb4>.39 | DReb6>.39 | DReb7>.39 then DRiatexcl=1;
  if DRiatexcl > 0 then do; DRiat = .; DRiat1 = .; DRiat2 = .; DRtradIAT = .; end;
  drop IAT IAT1 IAT2 subexcl mb3 mb4 mb6 mb7 CS1 CS2 AS1 AS2 EB3 EB4 EB6 EB7 FB3 FB4 FB6 FB7 FASTM DIFF1 DIFF2;
*proc means;run;

data study.iGS; set study.raw;
  if bname = 'straightgood' and trial < 22 then blockn = 'B3';
  else if bname = 'straightgood' and trial > 22 then blockn = 'B4';
  else if bname = 'straightbad' and trial < 22 then blockn = 'B6';
  else if bname = 'straightbad' and trial > 22 then blockn = 'B7';
  else delete;
%iatCalc(study, study, iGS, iGSclean, blockn, SUB, LAT, err, 2, 2, 1);
data study.iGSclean; set study.iGSclean;
  GSiat = IAT; GSiat1 = IAT1; GSiat2 = IAT2;
  GSiatexcl = subexcl; GStradIAT = DIFF2;
  GSmb3 = mb3; GSmb4 = mb4; GSmb6 = mb6; GSmb7 = mb7; 
  GSeb3 = eb3; GSeb4 = eb4; GSeb6 = eb6; GSeb7 = eb7; 
  if GSeb3>.39 | GSeb4>.39 | GSeb6>.39 | GSeb7>.39 then GSiatexcl=1;
  if GSiatexcl > 0 then do; GSiat = .; GSiat1 = .; GSiat2 = .; GStradIAT = .; end;
  drop IAT IAT1 IAT2 subexcl mb3 mb4 mb6 mb7 CS1 CS2 AS1 AS2 EB3 EB4 EB6 EB7 FB3 FB4 FB6 FB7 FASTM DIFF1 DIFF2;
*proc means;run;

data study.iTF; set study.raw;
  if bname = 'obesebad' and trial < 22 then blockn = 'B3';
  else if bname = 'obesebad' and trial > 22 then blockn = 'B4';
  else if bname = 'obesegood' and trial < 22 then blockn = 'B6';
  else if bname = 'obesegood' and trial > 22 then blockn = 'B7';
  else delete;
%iatCalc(study, study, iTF, iTFclean, blockn, SUB, LAT, err, 2, 2, 1);
data study.iTFclean; set study.iTFclean;
  TFiat = IAT; TFiat1 = IAT1; TFiat2 = IAT2;
  TFiatexcl = subexcl; TFtradIAT = DIFF2;
  TFmb3 = mb3; TFmb4 = mb4; TFmb6 = mb6; TFmb7 = mb7; 
  TFeb3 = eb3; TFeb4 = eb4; TFeb6 = eb6; TFeb7 = eb7; 
  if TFeb3>.39 | TFeb4>.39 | TFeb6>.39 | TFeb7>.39 then TFiatexcl=1;
  if TFiatexcl > 0 then do; TFiat = .; TFiat1 = .; TFiat2 = .; TFtradIAT = .; end;
  drop IAT IAT1 IAT2 subexcl mb3 mb4 mb6 mb7 CS1 CS2 AS1 AS2 EB3 EB4 EB6 EB7 FB3 FB4 FB6 FB7 FASTM DIFF1 DIFF2;
*proc means;run;

data compexp; informat major $20.;
  infile 'C:\primary\Data\Dissertation.2001\Diss.5\explicit.txt' delimiter='09'x;
  input pubpriv iatexp presub sub exptmntr $ susp year major $ sex ethnic $ Usborn howlong	
        english Ehowlong satmath satverb Estraight Ethin Erepubs Eobese Egay Edemos;

     DRexp1 = Edemos - Erepubs;TFexp1 = Ethin - Eobese;GSexp1 = Estraight - Egay;
     DRexp = mean(DRexp1);TFexp = mean(TFexp1);
 	 GSexp = mean(GSexp1);
proc sort; by sub;

data study.total; merge compexp study.iFIclean study.iDRclean study.iGSclean study.iTFclean; by sub;

proc means;run;

*implicit multi-trait;
proc corr; var FIiat DRiat GSiat TFiat;run;

*implicit internal consistency;
proc corr; var FIiat1 DRiat1 GSiat1 TFiat1;
           with FIiat2 DRiat2 GSiat2 TFiat2;run;

*explicit multi-trait;
proc corr; var DRexp GSexp TFexp;run;

*implicit-explicit comparison;
proc corr; var FIiat DRiat GSiat TFiat;
           with DRexp GSexp TFexp;run;



/*proc print; var sub TFiatexcl DRiatexcl GSiatexcl FIiatexcl;run;
proc print; where sub=510;run;
proc means;run;*/

/*proc means; var sc_pvt hm_pvt gy_pvt sp_pvt aa_pvt wh_pvt dm_pvt rp_pvt baa_pvt bdm_pvt bhm_pvt bsc_pvt bgy_pvt;run;
proc corr; var sc_pvt hm_pvt gy_pvt sp_pvt aa_pvt wh_pvt dm_pvt rp_pvt;run;
proc corr; var baa_pvt bdm_pvt bhm_pvt bsc_pvt bgy_pvt;run;
proc corr; var sc_pvt hm_pvt gy_pvt sp_pvt aa_pvt wh_pvt dm_pvt rp_pvt;
           with baa_pvt bdm_pvt bhm_pvt bsc_pvt bgy_pvt;run;*/